//
//  LPFComputator.h
//  LowPassFilter_Standalone
//
//  Created by kadlubek47 on xx.01.2013.
//  Copyright (c) 2013 kadlubek47. All rights reserved.
//

#ifndef __LowPassFilter_Standalone__LPFComputator__
#define __LowPassFilter_Standalone__LPFComputator__


#include <stdlib.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <vector>

#include "LPF_Types.h"
#include "ProcessInterface.h"


//! Class LPFComputator - Low Pass Filter Computator
/*!   
	real-time low-pass filter;
	audio-data comes to filter in blocks;
	many channel are supported;
	additional internal latency of method used is M sample;
*/
class LPFComputator
{
public:

    //Class defined type
    
    /*!
        EnumType to choose how perform convolution
    */
    enum convolutionMethodType {
        NORMAL_CPU = 0,
        //MULTITHREADED_CPU,
        OPENCL_GPU = 1
        //OPENCL_CPU
    };
    
    /*
        EnumType to choose way of envoking OpenCL code;
    */
    enum envokingOpenCLMethodType {
        GCD,    //Grand Central Dispatch
        STANDARD_API    //Standard OpenCL_API
    };
    
    
    
    //Creating, destroying
    
	/*!	
		in constructor there is call function setParameters -
		created object always represent some low-pass filter with specific parameters
	*/
	LPFComputator(int samplingRate, int fpass, int fstop, float_type oscillationInPass, float_type oscillationInStop, convolutionMethodType convolutionMethod, envokingOpenCLMethodType envokingOpenCLMethod = GCD);
	
    /*!
		h_ is allocated dynamically so it must be released;
	*/
	~LPFComputator(void);
    
    
    
    //Real Time Processing
    
    /*!
     sets filter parameters and calculate h(n)
     
     Function is used to set filter parameters.
     After assignments h(n) is calculated.
     */
	void setParameters( int samplingRate, int fpass, int fstop, float_type oscillationInPass, float_type oscillationInStop, convolutionMethodType convolutionMethod, bool first=false );
    
    /*
     process blocks for each channel
     
     /param outputBlock ptr to already alocated array
     */
	void process(float_type** inputBlock, float_type** outputBlock /* ptr to already alocated array */, int numOfSamples, int numOfChannels);
    
    /*
        to reset internal buffers containing samples from past call of function process
     */
    void reset();
    
    
    
    //Class ChannelProcessingUnit
    
    class ChannelProcessingUnit;
    friend class ChannelProcessingUnit;
    
    //! Class ChannelProcessingUnit
    /*
     process one noninterleaved channel
     */
    class ChannelProcessingUnit
    {
    public:
        ChannelProcessingUnit(LPFComputator& base);
        
        /*!
         process only one block from one channel
         
         processing is assumed to be real-time so successive blocks are coherent -
         to properly compute boundary samples there is need to keep history -
         data from previous blocks; function process must compute outputBlock
         from data in inputBlock+history and after computation put some data from inputBlock
         into history; Latency of output sample in comparison to input is M;
         */
        void process(float_type* inputBlock, int inputBlockSize, float_type* outputBlock);
    
        void reset();

        //! Class SignalWindow
        /*
         optimal when 2*M < inputBufferSize
         */
        class SignalWindow
        {
        public:
            float_type* signal_=NULL;
            unsigned int signalLength_=0;
            
            SignalWindow();
            
            ~SignalWindow();
            
            void setParameter(unsigned int M, unsigned int x_size);
            void update(float_type* x);
            void clear();
            
        private:
            unsigned int M_=0, x_size_=0;
        };
        
    private:

        LPFComputator* base_;
        
        /*!
            Samples from previous block(s)
         */
        SignalWindow signal_;
        
        /*!
         convolution of x(n) and h(n)
         
         |block of size M_| | samples for which result samples are computed | |block of size M_|: x(i+M_) -> y(i), 0 <= i <= sizeX-2*M
         stepping down and up are not get into considerations
         
         /param y >>out<<, ptr to already alocated array
         */
        void convoluteWithImpulseResponse (float_type* x, int sizeX, float_type* y/*out, ptr to already alocated array*/);
                
    };
    
    
    
    //Getting impulse response
    
	int getSizeOfImpulseResponse();
    
	/*!
     copy LowPassFilter::h_ to destination
     
     /param destination ptr to already alocated array, to alocate appriorate size of memory at first use getSizeOfImpulseResponse
     */
	void getImpulseResponse(float_type* destination);
    
    
    
private:
    
    //Filter parameters
    
    std::vector<ChannelProcessingUnit> channelProcessingUnits_;
    ProcessInterface* openclConvolutioner_;
    
    convolutionMethodType convolutionMethod_;                   //< how perform convolution

	int N_;         //!< length of impulse response
	int M_;         //!< half length of impulse response
	float_type* h_; //!< impulse response
	
    float_type oscillationInPass_;
	float_type oscillationInStop_;
    int fpass_, fstop_;
	int  samplingRate_;
	
	float_type normalizedPulsation_, normalizedFrequency_;
    
	
    
    //To compute impulse response
    
	/*!
		equation of ideal low pass filter;
		normalized pulsation is needed;
	*/
	float_type idealLowPassFilter(int sampleNum);
	
	static const int numElemApprox=32;              //!< number of elements which will approximate sum
	longer_float_type besselElem[numElemApprox];	//!< auxiliary table for I_O(float_type) function
	
	/*!
		compute auxiliary table for I_O(float_type) function;
		Expr = 2^k * k!; result will be put in besselElem table;
	*/
	void prepareBesselAprox();

	/*!
        zeroth order Modified Bessel function of the first kind
    
		aproximation made by >>numElemApprox<< first elements sumed
		computation done on longer_float_type
	*/
	float_type I_0(longer_float_type x);
    
    
    
}; /*class LPFComputator*/

#endif /* defined(__LowPassFilter_Standalone__LPFComputator
__) */

